package Runde4;

public abstract class StringTree{
	
	protected Node root;
	
	/**
	 * @precondition root.length() > 0 and root != NULL
	 * @postcondition Creates a new tree with given a root node, labeled with the given param
	 * @param root - StringTree's new root-node
	 * @throws InvalidParamException - if param root is NULL or an empty string
	 */
	public StringTree(String root) throws InvalidParamException {
		try {
			checkParam(root);
		} catch (InvalidParamException e) {
			throw e;
		}
		this.root = new Node(root);
	}
	
	/**
	 * @precondition node.length() > 0 and node != NULL
	 * @postcondition Returns true / false whether tree contains a node labeled with given String, or not
	 * @param node - node-label to look for
	 * @throws InvalidParamException - if param node is NULL or an empty string
	 */
	public boolean contains(String node) throws InvalidParamException {
		try {
			checkParam(node);
			if (search(node).equals("Node not found")) {
				return false;
			} 
			return true;
		} catch (InvalidParamException ex) {
			throw ex;
		}
	}
	
	/**
	 * @precondition node.length() > 0 and node != NULL
	 * @postcondition Returns path to node with label matching to the given string if found
     * @param node - node-label to look for
	 * @throws InvalidParamException - if param node is NULL or an empty string
	 */
	public String search(String node) throws InvalidParamException {
		try {
			checkParam(node);
			String result = depthSearch(node, root);
			if (result == null) {
				return "Node not found";
			}
			return result;
		} catch (InvalidParamException ex) {
			throw ex;
		}
	}
	
	/**
	 * @precondition wantedNode.length() > 0 and node,currentNode != NULL; Initial-call: currentNode should be
	 * 		StringTree's root in order to search in complete tree
	 * @postcondition Returns path to node with label matching to the given string if found;
	 * 		recursive support-method for "String search(String node)"
	 * @param wantedNode - node-label to look for
	 * @param currentNode - Node on which recursive method is applied on
	 */
	private String depthSearch(String wantedNode, Node currentNode) {
		if (currentNode == null) {
			return null;
		}
		
		String curStr = currentNode.toString();
		
		if (curStr.equals(wantedNode)) {
			return "";
		}
		String resultleft = depthSearch(wantedNode, currentNode.getLeftChild());
		String resultright = depthSearch(wantedNode, currentNode.getRightChild());
		if (!(resultleft == null)) {
			return "left " + resultleft;
		}
		if (!(resultright == null)) {
			return "right " + resultright;
		}
		return null;	
	}
		
	/**
	 * @precondition node.length() > 0 and node != NULL
	 * @postcondition Adds a node with given string as label
	 * @param node - label of the node that should be added
	 */
	public abstract void add(String node);
	
	/**
	 * @postcondition Returns a readable output of StringTree
	 */
	public String toString() {
		return recToString(root,"",0);
	}
	
	/**
	 * @precondition currentNode,output != null; Initial-call: output = "", depth = 0 and node should be root node of the tree
	 * 		in order to obtain the String-output of the complete StringTree
	 * @postcondition Returns a readable output of StringTree; recursive supportive-function for "String toString()"
	 * @param currentNode - Node on which recursive method is applied on
	 * @param output - String which is passed around to create String-output of the complete StringTree
	 * @param depth - represents the depth of the recursion and thus the depth of currentNode
	 */
	private String recToString(Node currentNode, String output, int depth) {
	    if (currentNode == null) {
	    	for (int i = 0; i < depth*2;i++) {
		    	output += " ";
		    }
	    	return output += "*\n";
	    }

	    for (int i = 0; i < depth*2;i++) {
	    	output += " ";
	    }
	    
	    if(currentNode != root) {
	    	output += "* ";
	    }
	    
	    output += currentNode + "\n";
	    if (currentNode.getLeftChild() == null && currentNode.getRightChild() == null) {
	    	return output;
	    }
	    String resultleft = recToString(currentNode.getLeftChild(), output, depth +1);
	    String resultright = recToString(currentNode.getRightChild(), resultleft, depth + 1);
	    return resultright;
	}
	
	/**
	 * @postcondition Checks the string. If it is valid as an argument, nothing happens. Otherwise an exception is thrown.
	 * @param param String to check
	 * @throws InvalidParamException If the argument is not valid, as in null or empty.
	 */
	protected void checkParam(String param) throws InvalidParamException {
		String parmsg = "Invalid param: ";
		if (param == null) {
			throw new InvalidParamException(parmsg + "NULL given");
		} 
		if (param.equals("")) {
			throw new InvalidParamException(parmsg + "empty label given");
		}
	}
}
